package com.wjk.kylin.id.core.generator.impl;

import com.wjk.kylin.id.core.entity.Result;
import com.wjk.kylin.id.core.entity.SegmentId;
import com.wjk.kylin.id.core.service.SegmentIdService;
import com.wjk.kylin.id.core.service.impl.HttpSegmentIdServiceImpl;
import com.wjk.kylin.id.core.util.NamedThreadFactory;
import com.wjk.kylin.id.core.entity.ResultCode;
import com.wjk.kylin.id.core.exception.IdSysException;
import com.wjk.kylin.id.core.generator.IdGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 *
 */
public class CachedIdGenerator implements IdGenerator {
    private static final Logger LOGGER = LoggerFactory.getLogger(CachedIdGenerator.class);

    private final SegmentIdService segmentIdService;
    //当前的id起始对象
    private volatile SegmentId current;
    //下一次的id起始对象
    private volatile SegmentId next;
    //是否在加载 下一次的id起始对象
    private volatile boolean isLoadingNext = false;
    private final Object lock = new Object();
    //异步请求next对象
    private final ExecutorService executorService = Executors.newSingleThreadExecutor(new NamedThreadFactory("kylin-id-generator"));

    //异步请求next对象 返回future
    private volatile Future<?> future;

    /**
     * @param segmentIdService
     */
    public CachedIdGenerator(HttpSegmentIdServiceImpl segmentIdService) {
        this.segmentIdService = segmentIdService;
        //
        this.loadCurrent();
    }

    /**
     * 加载当前id 起始对象
     */
    public synchronized void loadCurrent() {
        if (this.current == null || !this.current.isUse()) {
            if (this.next == null) {
                //有可能loadNext()还没结束
                if (this.isLoadingNext) {
                    //等待异步线程返回
                    try {
                        future.get();
                    } catch (Exception e) {
                        LOGGER.error("error query segmentId: {}", e.getMessage(), e);
                    }
                    if (this.next == null) {
                        this.current = this.querySegmentId();
                    } else {
                        this.current = this.next;
                        this.next = null;
                    }
                } else {
                    this.current = this.querySegmentId();
                }
            } else {
                this.current = this.next;
                this.next = null;
            }
        }
    }

    /**
     * 加载下一次id起始对象
     */
    public void loadNext() {
        if (this.next == null && !this.isLoadingNext) {
            synchronized (this.lock) {
                if (this.next == null && !this.isLoadingNext) {
                    future = this.executorService.submit(() -> {
                        this.next = this.querySegmentId();
                        this.isLoadingNext = false;
                        /**
                         * try {
                         *  // 无论获取下个segmentId成功与否，都要将isLoadingNext赋值为false
                         *  next = querySegmentId();
                         * } finally {
                         *  isLoadingNext = false;
                         * }
                         */
                    });
                    this.isLoadingNext = true;
                }
            }
        }
    }


    /**
     * 获取起始id对象
     *
     * @return
     */
    private SegmentId querySegmentId() {
        String message = null;

        try {
            SegmentId segmentId = this.segmentIdService.getNextSegmentId();
            if (segmentId != null) {
                return segmentId;
            }
        } catch (Exception e) {
            message = e.getMessage();
        }

        LOGGER.error("error query segmentId:{}", message);

        throw new IdSysException("error query segmentId: " + message);
    }


    /**
     * 获取下一个id
     *
     * @return
     */
    @Override
    public Long nextId() {
        while (true) {
            if (this.current != null) {
                Result result = this.current.nextId();
                //1:正常使用
                //2:提前去加载下一次的id请求
                //3:已经使用完id
                if (result.getCode() == ResultCode.LOADING.getCode()) {
                    this.loadNext();
                }
                if (result.getCode() != ResultCode.OVER.getCode()) {
                    return result.getId();
                }
            }
            this.loadCurrent();
        }
    }

    /**
     * 批量
     *
     * @param batchSize
     * @return
     */
    @Override
    public List<Long> nextId(Integer batchSize) {
        List<Long> ids = new ArrayList<>();

        for (int i = 0; i < batchSize; ++i) {
            Long id = this.nextId();
            ids.add(id);
        }

        return ids;
    }
}
